/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/pars/Parser.h"

//COMMON headers
#include "COMMON/tbox/PIO.h"

namespace DOGOS
{

#include "Grammar.cpp"
#include "Scanner.cpp"

Parser * Parser::s_default_parser = 0;
bool Parser::s_static_tables_initialized = 0;

/*
 *************************************************************************
 *
 * The constructor creates an unitialized parser object.  All of the
 * interesting work is done by member function parse().
 *
 *************************************************************************
 */

Parser::Parser()
{
    if (!s_static_tables_initialized)
    {
        parser_static_table_initialize();
        s_static_tables_initialized = 1;
    }
}

/*
 *************************************************************************
 *
 * The destructor automatically deallocates the parser object data.
 *
 *************************************************************************
 */

Parser::~Parser()
{}

/*
 *************************************************************************
 *
 * Begin parsing the input database file.  Return the number of errors
 * encountered in the parse.
 *
 *************************************************************************
 */

int
Parser::parse(
    const std::string& file_name,
    FILE* fstream,
    InputDatabase* database)
{
    d_errors = 0;
    d_warnings = 0;

    // Find the path in the file_name, if one exists
    std::string::size_type slash_pos = file_name.find_last_of('/');
    if (slash_pos == std::string::npos)
    {
        d_pathname = "";
    }
    else
    {
        d_pathname = file_name.substr(0, slash_pos + 1);
    }

    ParseData pd;
    pd.d_file_name = file_name;
    pd.d_fstream = fstream;
    pd.d_linenumber = 1;
    pd.d_cursor = 1;
    pd.d_nextcursor = 1;
    d_parse_stack.clear();
    d_parse_stack.push_front(pd);

    d_scope_stack.clear();
    d_scope_stack.push_front (database);

    s_default_parser = this;
    yyrestart(0);
    if (yyparse() && (d_errors == 0))
    {
        error("Unexpected parse error");
    }
    s_default_parser = 0;

    d_parse_stack.clear();
    d_scope_stack.clear();

    return d_errors;
}

/*
 *************************************************************************
 *
 * Advance the cursor to the next line in the current input file.
 *
 *************************************************************************
 */

void
Parser::advance_line(
    const int nline)
{
    Parser::ParseData& pd = d_parse_stack.front();
    pd.d_linenumber += nline;
    pd.d_cursor = 1;
    pd.d_nextcursor = 1;
}

/*
 *************************************************************************
 *
 * Advance the cursor position by the token in the specified string.
 * Tabs are expanded assuming tab stops at eight character markers.
 *
 *************************************************************************
 */

void
Parser::advance_cursor(
    const std::string& token)
{
    Parser::ParseData& pd = d_parse_stack.front();
    pd.d_cursor = pd.d_nextcursor;
    for (std::string::const_iterator i = token.begin(); i != token.end(); i++)
    {
        if (*i == '\t')
        {
            pd.d_nextcursor = ((pd.d_nextcursor + 7) & (~7)) + 1;
        }
        else
        {
            pd.d_nextcursor++;
        }
    }
}

/*
 *************************************************************************
 *
 * Print out errors to POUT and track the number of errors.
 *
 *************************************************************************
 */

void
Parser::error(
    const std::string& message)
{
    Parser::ParseData& pd = d_parse_stack.front();

    POUT << "Error in " << pd.d_file_name << " at line " << pd.d_linenumber
         << " column " << pd.d_cursor
         << " : " << message << std::endl << std::flush;

    POUT << pd.d_linebuffer << std::endl << std::flush;

    for (int i = 0; i < pd.d_cursor; i++)
        POUT << " ";
    POUT << "^\n";

    d_errors++;
}

/*
 *************************************************************************
 *
 * Print out warnings to POUT and track the number of warnings.
 *
 *************************************************************************
 */

void
Parser::warning(
    const std::string& message)
{
    Parser::ParseData& pd = d_parse_stack.front();

    POUT << "Warning in " << pd.d_file_name << " at line " << pd.d_linenumber
         << " column " << pd.d_cursor
         << " : " << message << std::endl << std::flush;

    POUT << pd.d_linebuffer << std::endl << std::flush;

    for (int i = 0; i < pd.d_cursor; i++)
        POUT << " ";
    POUT << "^\n";

    d_warnings++;
}

/*
 *************************************************************************
 *
 * Iterate through the database scopes, looking for the first match on
 * the key value.
 *
 *************************************************************************
 */

InputDatabase*
Parser::get_database_with_key(
    const std::string& key)
{
    std::list<InputDatabase*>::iterator i = d_scope_stack.begin();
    for (; i != d_scope_stack.end(); i++)
    {
        if ((*i)->key_exists(key))
        {
            return *i;
        }
    }
    return NULL;
}

/*
 *************************************************************************
 *
 * Create a new parse state on the parse stack and open the specified
 * new file for reading.
 *
 *************************************************************************
 */

bool
Parser::push_include_file(
    const std::string& file_name)
{
    FILE* fstream = 0;

    std::string file_name_with_path;

    // If this is not a fully qualified pathname use
    // current search path
    std::string::size_type slash_pos;
    slash_pos = file_name.find_first_of('/');
    if (slash_pos == 0)
    {
        file_name_with_path = file_name;
    }
    else
    {
        file_name_with_path = d_pathname;
        file_name_with_path += file_name;
    }

    fstream = fopen(file_name_with_path.c_str(), "r");

    int worked = (fstream ? 1 : 0);

    if (!worked)
    {
        error("Could not open include file ``" + file_name_with_path + "''");
    }
    else
    {
        ParseData pd;
        pd.d_file_name = file_name_with_path;
        pd.d_fstream = fstream;
        pd.d_linenumber = 1;
        pd.d_cursor = 1;
        pd.d_nextcursor = 1;
        d_parse_stack.push_front(pd);
    }

    return worked ? true : false;
}

/*
 *************************************************************************
 *
 * Close the current input file and pop the parse stack.
 *
 *************************************************************************
 */

void
Parser::pop_include_file()
{
    Parser::ParseData& pd = d_parse_stack.front();
    if (pd.d_fstream)
    {
        fclose(pd.d_fstream);
    }
    d_parse_stack.pop_front();
}

/*
 *************************************************************************
 *
 * Manage the input reading for the flex scanner.  If running with MPI,
 * the node zero reads the data and broadcasts the length and the data
 * to all processors.
 *
 *************************************************************************
 */

int
Parser::yyinput(
    char* buffer,
    const int max_size)
{
    int byte = static_cast<int>(fread(buffer,
                                      1,
                                      max_size,
                                      d_parse_stack.front().d_fstream));

    return byte;
}

}
